#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/imu/icm42686.h>
#include <asm/neon.h>
#include <linux/kthread.h>
#include <linux/workqueue.h>

struct iio_dev *icm42686dev_iio = NULL;
struct icm42686 *icm42686_bottom_dev;
struct work_struct key_bd;
struct spi_device	*spidev_bottom = NULL;

static void work_queue_icm42686_bottom(struct work_struct *arg);

/* 陀螺仪分辨率 */
const int icm42686_gyro_scale_tbl_bottom[] = {
     500U * 1000000 / 65536, 
    1000U * 1000000 / 65536,
    2000U * 1000000 / 65536,
    4000U * 1000000 / 65536,
};

/* 加速度计分辨率 */
const int icm42686_accel_scale_tbl_bottom[] = {
	 2ULL * 1000000000 / 65536,
     4ULL * 1000000000 / 65536,
     8ULL * 1000000000 / 65536,
    16ULL * 1000000000 / 65536,
    32ULL * 1000000000 / 65536,
};

static const struct iio_chan_spec icm42686_channels[] = {
    ICM42686_TEMP_CHAN(IIO_TEMP, ICM42686_SCAN_TEMP),
    ICM42686_CHAN(IIO_ANGL_VEL, IIO_MOD_X, ICM42686_SCAN_GYRO_X),
    ICM42686_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, ICM42686_SCAN_GYRO_Y),
    ICM42686_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, ICM42686_SCAN_GYRO_Z),
    ICM42686_CHAN(IIO_ACCEL, IIO_MOD_X, ICM42686_SCAN_ACCEL_X),
    ICM42686_CHAN(IIO_ACCEL, IIO_MOD_Y, ICM42686_SCAN_ACCEL_Y),
    ICM42686_CHAN(IIO_ACCEL, IIO_MOD_Z, ICM42686_SCAN_ACCEL_Z),
};

static void icm42686_sensor_bluk_read(struct icm42686 *dev, u32 addr, u32 * data, u8 read_bytes)
{
	int i = 0;
    //__be16 d;

	for (i = 0; i < read_bytes; i++) {
        //regmap_bulk_read(dev->regmap, addr+i, (u8 *)&d, 1);
        //printk("---bulk[%d] = 0x%02x", i, (d & 0xff));
        //data[i] = (d & 0xff);
            
		regmap_read(dev->regmap, addr + i, &data[i]);
        //printk("---data[%d] = 0x%02x", i, data[i]);
	}
}

static void icm42686_acc_gyro(struct icm42686 *dev, u32 * data, u32 read_len )
{	
	#if 0
	int value = 0;
	int *val = &value;
	icm42686_sensor_read(dev, ACCEL_OUT_ADDR, IIO_MOD_X, val);
	icm42686_sensor_read(dev, ACCEL_OUT_ADDR, IIO_MOD_Y, val);
	icm42686_sensor_read(dev, ACCEL_OUT_ADDR, IIO_MOD_Z, val);

	icm42686_sensor_read(dev, GYRO_OUT_ADDR, IIO_MOD_X, val);
	icm42686_sensor_read(dev, GYRO_OUT_ADDR, IIO_MOD_Y, val);
	icm42686_sensor_read(dev, GYRO_OUT_ADDR, IIO_MOD_Z, val);
	#endif

	icm42686_sensor_bluk_read(dev, ACCEL_OUT_ADDR, data, read_len);
}

int icm42686_get_data_bottom_bd(struct icm42686 *dev, struct icm42686_data * imu)
{
	u32 data[12] = {0};

	icm42686_acc_gyro(dev, data, 12);
	imu->acc.x = (data[0] & 0xff) << 8 | (data[1] & 0xff);
	imu->acc.y = (data[2] & 0xff) << 8 | (data[3] & 0xff);
	imu->acc.z = (data[4] & 0xff) << 8 | (data[5] & 0xff);
	imu->gyro.x = (data[6] & 0xff) << 8  | (data[7] & 0xff);
	imu->gyro.y = ((data[8] & 0xff) << 8)  | (data[9] & 0xff);
	imu->gyro.z = (data[10] & 0xff) << 8 | (data[11] & 0xff);

    /*printk("0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x\n", 
                            imu->acc.x, imu->acc.y, imu->acc.z,
                            imu->gyro.x, imu->gyro.y, imu->gyro.z);*/

	return 0;
}

static ssize_t
spidev_sync_bottom(struct spi_device *spidev, struct spi_message *message)
{
	int status;
	struct spi_device *spi;

	spi = spidev;

	if (spi == NULL)
		status = -ESHUTDOWN;
	else
		status = spi_sync(spi, message);

	if (status == 0)
		status = message->actual_length;

	return status;
}

static inline ssize_t
spidev_sync_bottom_read(struct spi_device *spidev, struct icm42686_data * imu, size_t len)
{
	ssize_t			status = 0;
	u8 tx_buf[1] = {0x1f | 0x80};
	u8 rx_buf[13] = {0};
    struct timeval systime = {0};
    unsigned long long system_time;

	struct spi_transfer	t = {
			.rx_buf		= rx_buf,
			.tx_buf		= tx_buf,
			.len		= len,
			.speed_hz	= 24000000,
            .bits_per_word = 8,
            .delay_usecs = 0,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);

    do_gettimeofday(&(systime)); 
    system_time = systime.tv_sec * 1000 + systime.tv_usec / 1000;

	status = spidev_sync_bottom(spidev, &m);
	if (status > 0) {
        imu->sys_timestamp = system_time;
        imu->acc.x  = rx_buf[1]<<8  | rx_buf[2];
        imu->acc.y  = rx_buf[3]<<8  | rx_buf[4];
        imu->acc.z  = rx_buf[5]<<8  | rx_buf[6];
        imu->gyro.x = rx_buf[7]<<8  | rx_buf[8];
        imu->gyro.y = rx_buf[9]<<8  | rx_buf[10];
        imu->gyro.z = rx_buf[11]<<8 | rx_buf[12];

        /*printk("b %lld, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x\n", system_time, 
            rx_buf[1]<<8|rx_buf[2], rx_buf[3]<<8|rx_buf[4], rx_buf[5]<<8| rx_buf[6],
            rx_buf[7]<<8|rx_buf[8], rx_buf[9]<<8|rx_buf[10], rx_buf[11]<<8|rx_buf[12]);*/
	}

	return 0;
}

struct icm42686_data bo_imu;
static struct icm42686_data * bd_imu = NULL;

int icm42686_get_data_bottom(struct icm42686 *dev, struct icm42686_data * imu)
{	
    /*struct timeval systime = {0};
    unsigned long long system_time; */

    bd_imu = imu;

    /*do_gettimeofday(&(systime)); 
    system_time = systime.tv_sec * 1000 + systime.tv_usec / 1000;
    printk("b %lld\n", system_time);*/

	schedule_work(&key_bd);

	return 0;
}

EXPORT_SYMBOL(bo_imu);
EXPORT_SYMBOL(icm42686_bottom_dev);
EXPORT_SYMBOL(icm42686_get_data_bottom);

/* work queue */
static void work_queue_icm42686_bottom(struct work_struct *arg)
{
    #if 1	
	if (spidev_bottom != NULL)
		//spidev_sync_bottom_read(spidev_bottom, bd_imu, 13);
        spidev_sync_bottom_read(spidev_bottom, &bo_imu, 13);
	else
		printk("spidev_bottom is null.\n");
	#endif

    #if 0
	if (icm42686_bottom_dev != NULL)
		icm42686_get_data_bottom_bd(icm42686_bottom_dev, bd_imu);
	else
		printk("icm42686_bottom_dev is null.\n");	
    #endif
}

/*
 * icm42686内部寄存器初始化函数
 * @param   : none
 * @return  : noreturn
 */
static void icm42686_reg_init(struct icm42686 *dev)
{
    int ret;

    regmap_write(dev->regmap, REG_BANK_SEL, 0x00);
    mdelay(50);

    regmap_write(dev->regmap, DEVICE_CONFIG, 0x00);
    mdelay(50);

    if (regmap_read(dev->regmap, WHO_AM_I, &ret) >= 0)
        printk("--- icm42686 ID = %#x ---\r\n", ret);
	/*
	* GYRO_CONFIG0:Gyro Full scale and sampling rate config
	* GYRO_FS_SEL[bit5-7],GYRO_ODR[bit0-3],page 58 of 105
	* GYRO_FS_SEL:
		0x00:+-4000dps(default); 0x02:+-2000dps; 0x40:+-1000dps;0x60:+-500dps 
	* GYRO_ODR:
		0x00:Reserved; 0x01:32KHz; 0x02:16KHz; 0x03:8KHz; 0x04:4KHz; 
		0x05:2KHz; 0x06:1KHz(default); 0x07:200Hz; 0x08:100Hz; 
	*/
    regmap_write(dev->regmap, GYRO_CONFIG0,  0x26);	//+-2000dps//1KHz

	/*
	* GYRO_CONFIG1: address 0x51; default:0x16
	* TEMP_FILT_BW:[bit5-7],GYRO_UI_FILT_ORD:[bit2-3],GYRO_DEC2_M2_ORD:[bit0-1]
	* default value: DLPF BW=4000Hz,DLPF Latency=0.125ms
	*	UI filt ord:2th order; gyro_dec2_m2_ord:3th order
	*/
    regmap_write(dev->regmap, GYRO_CONFIG1,  0x16);

	/*
	* ACCEL_CONFIG0
	* ACCEL_FS_SEL[bit5-7] and ACCEL_ODR[bit0-3]) address=0x50,value default 0x06
	* ACCEL_FS_SEL: 000: +-32g; 001:+-16g; 010:+-8g; 011:+-4g; 100:+-2g; 
	* ACCEL_ODR:0001:32KHz; 0010:16KHz; 0011: 8KHz; 0100: 4KHz;0101: 2KHz;0110: 1KHz;
	*	0111: 200Hz; 1000: 1000Hz; 1001:50Hz; 1010:25Hz 
	*/
    regmap_write(dev->regmap, ACCEL_CONFIG0,  0x06);/* full scale: +-32g,accel sampling rate: 1KHz*/

    /*
	* ACCEL_CONFIG1:order for ACCEL_UI_FILT_ORD[bit3-4] and ACCEL_DEC2_M2_ORD [bit1-2]
	* Address:0x53,default value:0x0D
	* ACCEL_UI_FILT_ORD: 00:1th ; 01:2th; 10:3th; 11:reserved 
	* ACCEL_DEC2_M2_ORD: 00:1th ; 01:2th; 10:3th; 11:reserved 
	*/
	regmap_write(dev->regmap, ACCEL_CONFIG1,  0x0D);

	/*
	* GYRO_ACCEL_CONFIG0: bandwidth for accel LPF and Gyro LPF; 
	* Address=0x52,default value:0x11; 
	* ACCEL_UI_FILT_BW:[bit4-7],default:1,BW=max(400Hz,ODR)/4
	* GYRO_UI_FILT_BW:[bit0-3],default:1,BW=max(400Hz,ODR)/4
	*/
	regmap_write(dev->regmap, GYRO_ACCEL_CONFIG0, 0x00);//1st order	P76; R-> 0x52=0x00

	/* 
	* INT1:active low,open drain,pulsed mode
	* INT2:active low,open drain,pulsed mode
	*/
	regmap_write(dev->regmap, INT_CONFIG, 0x00);

	/* stream-to-fifo mode */
	regmap_write(dev->regmap, FIFO_CONFIG, 0x01<<6);
	
	/* Disables de-assert duration, Otherwise, output 4K frequency */
	regmap_write(dev->regmap, INT_CONFIG1, 0x60);	

	/* TMST_TO_REGS_EN:1,TMST_FSYNC_EN:1,TMST_EN:1 */
	regmap_write(dev->regmap, TMST_CONFIG, 0x13);

	/* FIFO count in records pakage of data,
	FIFO count and data are reported in big endian format  
	*/
	regmap_write(dev->regmap, INTF_CONFIG0, 0x70);

	/* set UI data ready interrupt touted to INT1 */
	regmap_write(dev->regmap, FIFO_SOURCE0, 0x08);

	/*
	* FIFO_CONFIG1: Address=0x5F,default value:0x00; 
	* bit0:FIFO_ACCEL_EN; 
	* bit1:FIFO_GYRO_EN; 
	* bit2:FIFO_TEMP_EN; 
	*/
	regmap_write(dev->regmap, FIFO_CONFIG1, FIFO_CONFIG1_DATA);//GYRO and ACCEL FIFO enable	R-> 0x5f=0x03

	/*
	* PWR_MGMT0: Address=0x4E,default value:0x00; 
	* ACCEL_MODE[bit0-1]:11:Places accelerometer in Low Noise (LN) Mode
	* GYRO_MODE[bit2-3]:11:Places gyroscope in Low Noise (LN) Mode
	*/
	regmap_write(dev->regmap, PWR_MGMT0, PWR_MGMT0_DATA);	// 1st order	P72	R->0x4e=0x0f
}

/*
 * @brief 读取icm42686传感器数据
 * 
 * @param dev icm42686设备
 * @param reg 要读取通道寄存器首地址
 * @param ch  通道序号
 * @param val 保存读取的值
 * @return    0, 成功;  其它值, 错误
 */
static int icm42686_sensor_read(struct icm42686 *dev, int reg, int ch, int *val)
{
    int ind, result;
    __be16 d;

    ind = (ch - IIO_MOD_X) * 2;
    result = regmap_bulk_read(dev->regmap, reg + ind, (u8 *)&d, 2);
    if (result)
        return -EINVAL;

    *val = (short)be16_to_cpup(&d);
	printk("--- 0x%02x 0x%02x ---\n", *val, d);

    return IIO_VAL_INT;
}

static int icm42686_read_raw(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *chan,
			      int *val, int *val2,
			      long mask)
{
    int ret;
    struct icm42686 *dev = iio_priv(indio_dev);

    switch (mask) {
    case IIO_CHAN_INFO_RAW: /* 读取传感器数据 */
        switch (chan->type) {
        case IIO_ANGL_VEL:  /* 读取陀螺仪数据 */
            mutex_lock(&dev->lock);
            ret = icm42686_sensor_read(dev, GYRO_OUT_ADDR, chan->channel2, val);
            mutex_unlock(&dev->lock);
            break;

        case IIO_ACCEL:  /* 读取加速度计数据 */
            mutex_lock(&dev->lock);
            ret = icm42686_sensor_read(dev, ACCEL_OUT_ADDR, chan->channel2, val);
            mutex_unlock(&dev->lock);
            break;

        case IIO_TEMP:  /* 读取温度数据 */
            mutex_lock(&dev->lock);
            ret = icm42686_sensor_read(dev, TEMP_OUT_ADDR, chan->channel2, val);
            mutex_unlock(&dev->lock);
            break;

        default:
            ret = -EINVAL;
            break;
        }
        break;

    case IIO_CHAN_INFO_OFFSET:
        switch (chan->type) {
        case IIO_TEMP:
            mutex_lock(&dev->lock);
            *val = 0;
            ret = IIO_VAL_INT;
            mutex_unlock(&dev->lock);
            break;

        default:
            ret = -EINVAL;
            break;
        }
        break;

    case IIO_CHAN_INFO_SCALE:
        switch (chan->type) {
        case IIO_TEMP:
            mutex_lock(&dev->lock);
            *val = 326800000 / 1000000;
            *val2 = 326800000 % 1000000;
            ret = IIO_VAL_INT_PLUS_MICRO;
            mutex_unlock(&dev->lock);
            break;
            
        case IIO_ANGL_VEL:
            mutex_lock(&dev->lock);
            regmap_read(dev->regmap, GYRO_CONFIG0, &ret);
            *val = 0;
            *val2 = icm42686_gyro_scale_tbl_bottom[(ret & 0x18) >> 3];
            ret = IIO_VAL_INT_PLUS_MICRO;
            mutex_unlock(&dev->lock);
            break;
        
        case IIO_ACCEL:
            mutex_lock(&dev->lock);
            regmap_read(dev->regmap, ACCEL_CONFIG0, &ret);
            *val = 0;
            *val2 = icm42686_accel_scale_tbl_bottom[(ret & 0x18) >> 3];
            printk("Read accel scale index value: %d\n", icm42686_accel_scale_tbl_bottom[(ret & 0x18) >> 3]);
            ret = IIO_VAL_INT_PLUS_NANO;
            mutex_unlock(&dev->lock);
            break;
        
        default:
            ret = -EINVAL;
            break;
        }
        break;

    default:
        ret = -EINVAL;
        break;
    }
    
    return ret;
}

static int icm42686_write_raw(struct iio_dev *indio_dev,
			 struct iio_chan_spec const *chan,
			 int val,
			 int val2,
			 long mask)
{
    int ret = 0;
    int i, d;
    struct icm42686 *dev = iio_priv(indio_dev);
    
    switch (mask) {
    case IIO_CHAN_INFO_SCALE:
        switch (chan->type) {
        case IIO_ANGL_VEL:
            mutex_lock(&dev->lock);
            for(i = 0; i < ARRAY_SIZE(icm42686_gyro_scale_tbl_bottom); ++i) 
                if (icm42686_gyro_scale_tbl_bottom[i] == val2) 
                    break;

            if (i < ARRAY_SIZE(icm42686_gyro_scale_tbl_bottom)) {
                d = (i << 3);
                ret = regmap_write(dev->regmap, GYRO_CONFIG0, d);
            } else
                ret = -EINVAL;
            mutex_unlock(&dev->lock);
            break;

        case IIO_ACCEL:
            mutex_lock(&dev->lock);
            for(i = 0; i < ARRAY_SIZE(icm42686_accel_scale_tbl_bottom); ++i) 
                if (icm42686_accel_scale_tbl_bottom[i] == val2) 
                    break;

            if (i < ARRAY_SIZE(icm42686_accel_scale_tbl_bottom)) {
                d = (i << 3);
                ret = regmap_write(dev->regmap, ACCEL_CONFIG0, d);
            } else
                ret = -EINVAL;
            mutex_unlock(&dev->lock);
            break;

        default:
            ret = -EINVAL;
            break;
        }
        break;
   
    default:
        ret = -EINVAL;
        break;
    }
    return ret;
}

static int icm42686_write_raw_get_fmt(struct iio_dev *indio_dev,
			 struct iio_chan_spec const *chan,
			 long mask)
{
    switch (mask) {
    case IIO_CHAN_INFO_SCALE:
        switch (chan->type) {
        case IIO_TEMP:
        case IIO_ANGL_VEL:
            return IIO_VAL_INT_PLUS_MICRO;

        case IIO_ACCEL:
            return IIO_VAL_INT_PLUS_NANO;
        
        default:
            return IIO_VAL_INT_PLUS_MICRO;
        }

    default:
        return IIO_VAL_INT_PLUS_MICRO;
    }
    return -EINVAL;
}

static const struct iio_info icm42686_info = {
    .read_raw = &icm42686_read_raw,
    .write_raw = &icm42686_write_raw,
    .write_raw_get_fmt = &icm42686_write_raw_get_fmt,
};

/*
 * spi驱动的probe函数, 当驱动与设备匹配会执行此函数
 * @param   client: spi设备
 * @param   id:     spi设备ID
 */
static int icm42686_probe(struct spi_device *spi)
{
    int ret = 0;
    //struct icm42686 *icm42686_bottom_dev;
    struct iio_dev *iiodev;
    #if 0
    struct icm42686_data imu_test = {0};
    #endif

	printk("--- icm42686 bottom probe 1---\n");

    /* 1.向内核申请分配iio_dev内存, 包括同时分配的icm42686_dev内存 */
    iiodev = devm_iio_device_alloc(&spi->dev, sizeof(struct icm42686));
    if (!iiodev) {
        return -ENOMEM;
    }
    spi_set_drvdata(spi, iiodev);

    /* 2.把已分配的indio_dev内存结构的私有数据赋给icm42686_dev */
    icm42686_bottom_dev = iio_priv(iiodev);
    icm42686_bottom_dev->spi = spi;
    mutex_init(&icm42686_bottom_dev->lock);

    spidev_bottom = spi;

    /* 3.设置iio_dev的主要成员变量 */
    iiodev->name = "icm42686_bottom";
    iiodev->dev.parent = &spi->dev;
    iiodev->info = &icm42686_info;
    iiodev->modes = INDIO_DIRECT_MODE;
    iiodev->channels = icm42686_channels;
    iiodev->num_channels = ARRAY_SIZE(icm42686_channels);

    /* 4.注册iio_dev */
    ret = iio_device_register(iiodev);
    if (ret < 0) {
        dev_err(&spi->dev, "iio_device_register failed\n");
        goto err_iio_register;
    }        

    /* 5.初始化regmap_config配置 */
    icm42686_bottom_dev->regmap_cfg.reg_bits = 8;           /* 寄存器长度 */
    icm42686_bottom_dev->regmap_cfg.val_bits = 8;           /* 值长度 */
    icm42686_bottom_dev->regmap_cfg.read_flag_mask = 0x80;  /* 读掩码 */

    /* 6.初始化SPI接口的regmap */
    icm42686_bottom_dev->regmap = regmap_init_spi(spi, &icm42686_bottom_dev->regmap_cfg);
    if (IS_ERR(icm42686_bottom_dev->regmap)) {
        ret = PTR_ERR(icm42686_bottom_dev->regmap);
        goto err_regmap_init;
    }

    /* 初始化spi_device */
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    spi->max_speed_hz = 24000000;
    ret = spi_setup(spi);
    icm42686_bottom_dev->spi = spi;
    //spi_set_drvdata(spi, icm42686_bottom_dev);

    /* 初始化icm42686内部寄存器 */
    icm42686_reg_init(icm42686_bottom_dev);

	INIT_WORK(&key_bd, work_queue_icm42686_bottom);

    #if 0
    while (1) {
        //icm42686_get_data_bottom(icm42686_bottom_dev, &imu_test);
        icm42686_get_data_bottom(NULL, &imu_test);
    }
    #endif

    return 0;

err_regmap_init:
    iio_device_unregister(iiodev);
err_iio_register:
    //kzfree(icm42686_bottom_dev);
    regmap_exit(icm42686_bottom_dev->regmap);
    return ret;
}

/**
 * spi驱动的remove函数，移除spi驱动的时候此函数会执行
 * @param   : client spi设备
 * @return  : 0 成功; 负值 失败
 */
static int icm42686_remove(struct spi_device *spi)
{    
    struct iio_dev *iiodev = spi_get_drvdata(spi);
    struct icm42686 *dev; 
    
    dev = iio_priv(iiodev);

    /* 删除regmap */
    regmap_exit(dev->regmap);

    /* 注销IIO */
    iio_device_unregister(iiodev);
    //kzfree(dev);

    return 0;
}

static const struct spi_device_id icm42686_bottom_id[] = {
    {"tdk,icm42686_bottom", 0},
    {}
};
MODULE_DEVICE_TABLE(spi, icm42686_bottom_id);

static const struct of_device_id icm42686_bottom_of_match[] = {
    {.compatible = "tdk,icm42686_bottom"},
    { /* Sentinel */}
};
MODULE_DEVICE_TABLE(of, icm42686_bottom_of_match);

static struct spi_driver icm42686_bottom_driver = {
    .probe  = icm42686_probe,
    .remove = icm42686_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name  = "icm42686_bottom",
        .of_match_table = icm42686_bottom_of_match,
    },
    .id_table = icm42686_bottom_id,
};

static int __init icm42686_bottom_init(void)
{
	printk("--- icm42686_bottom_init ---\n");
    return spi_register_driver(&icm42686_bottom_driver);
}

static void __exit icm42686_bottom_exit(void)
{
    spidev_bottom = NULL;
    spi_unregister_driver(&icm42686_bottom_driver);
}

module_init(icm42686_bottom_init);
module_exit(icm42686_bottom_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ac");
